/*
 * Copyright (c) 2011, Nikolaus Moll
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * * Neither the name of the gradecalculator.net nor the
 *   names of its contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */
package net.gradecalculator.gui.qt.calculator.models;

import java.util.ArrayList;
import java.util.List;

import net.gradecalculator.core.AdaptedRegulations;
import net.gradecalculator.core.adapted.AdaptedModule;
import net.gradecalculator.core.interfaces.ICourse;
import net.gradecalculator.core.interfaces.IElectiveCourse;
import net.gradecalculator.core.interfaces.IModule;
import net.gradecalculator.core.interfaces.results.IResult;
import net.gradecalculator.core.interfaces.results.IResultType;
import net.gradecalculator.core.util.ECTSUtil;
import net.gradecalculator.core.util.ResultTypeListUtil;
import net.gradecalculator.gui.qt.common.models.AbstractMultiColumnTreeModel;
import net.gradecalculator.gui.qt.common.widgets.TreeIndex;

import com.trolltech.qt.core.Qt;
import com.trolltech.qt.gui.QBrush;
import com.trolltech.qt.gui.QColor;
import com.trolltech.qt.gui.QFont;
import com.trolltech.qt.gui.QWidget;

public class GradesTreeModel extends AbstractMultiColumnTreeModel
{
    private static final int COLNAME = 0;
    private static final int COLECTS = 1;
    private static final int COLGRADES = 2;

    private static final String PASSED = "pass";

    private static final int ALRIGHT = Qt.AlignmentFlag.createQFlags(Qt.AlignmentFlag.AlignRight,
            Qt.AlignmentFlag.AlignVCenter).value();
    private static final int ALLEFT = Qt.AlignmentFlag.createQFlags(Qt.AlignmentFlag.AlignLeft,
            Qt.AlignmentFlag.AlignVCenter).value();

    private final AdaptedRegulations regulations;
    private final int gradeColumnCount;
    private final int columnCount;
    private final QFont fontModule;
    private final QFont fontCourse;

    private final QWidget parent;
    private final List<IResultType> resultTypes;

    public GradesTreeModel(final QWidget parent, final AdaptedRegulations regulations)
    {
        this.regulations = regulations;
        this.parent = parent;
        this.resultTypes = regulations.getStudent().getResultTypes();
        registerMimeClass(IResult.class);

        fontModule = new QFont(parent.font().family(), parent.font().pointSize(), QFont.Weight.Bold.value(), false);
        fontCourse = new QFont(parent.font().family(), parent.font().pointSize(), QFont.Weight.Normal.value(), false);

        gradeColumnCount = resultTypes.size();
        columnCount = COLGRADES + gradeColumnCount;
    }

    private int getModuleCount()
    {
        return regulations.getModules().size();
    }

    @Override
    public int getColumnCount()
    {
        return columnCount;
    }

    @Override
    public QFont getFont(final Object data)
    {
        if (data instanceof ICourse) {
            return fontCourse;
        }

        if (data instanceof IResult) {
            return fontCourse;
        }

        return fontModule;
    }

    @Override
    public QBrush getBrush(final Object data)
    {
        if (data instanceof AdaptedModule) {
            // TODO
            // final StudentModule module = (StudentModule) data;
            // final float reachedECTS =
            // module.getECTS(ExaminationResultType.PASSED);
            // final float moduleECTS = module.getModule().getECTS();
            // return createBrush(reachedECTS, moduleECTS);
        }

        if (data instanceof ICourse) {
            // TODO
            // final Course course = (Course) data;
            // final ExaminationResult examiniationResult =
            // regulations.getStudent().getResult(course);
            // final Helper.PassResult passResult = Helper.getPassed(course,
            // examiniationResult);
            // return createBrush(passResult.ordinal(),
            // Helper.PassResult.values().length);
        }

        return new QBrush(QColor.black);
    }

    protected static QBrush createBrush(final float value, final float max)
    {
        if (Math.abs(value - max) < 0.1) {
            return new QBrush(QColor.black);
        }

        final int minColor = 135;
        final int maxColor = 220;

        final float percent = value / max;
        final int color = 255 - (int) ((maxColor - minColor) * percent + minColor);

        return new QBrush(new QColor(color, color, color));
    }

    @Override
    public int getAlignment(final int column)
    {
        switch (column) {
        case COLECTS:
            return ALRIGHT;

        default:
            return ALLEFT;

        }
    }

    @Override
    public int getRowCount(final TreeIndex index)
    {
        if (index == null) {
            // root
            final int unassignedResultsRows;
            if (hasUnassignedResults()) {
                unassignedResultsRows = 1;
            }
            else {
                unassignedResultsRows = 0;
            }
            return 2 + getModuleCount() + unassignedResultsRows;
        }

        if (index.getParent() != null) {
            return 0;
        }

        if (isModuleRow(index.getRow())) {
            final AdaptedModule module = regulations.getModules().get(index.getRow() - 1);
            return module.getModule().getCourses().size();
        }

        if (isUnassignedResultRow(index.getRow())) {
            return regulations.getUnassignedResults().size();
        }

        return 0;
    }

    private boolean hasUnassignedResults()
    {
        return regulations.getUnassignedResults().size() > 0;
    }

    @Override
    public List<String> getHeaderLabels()
    {
        final List<String> result = new ArrayList<String>(getColumnCount());
        for (int i = 0; i < getColumnCount(); i++) {
            result.add(getHeaderText(i));
        }
        return result;
    }

    @Override
    public String getHeaderText(final int column)
    {
        switch (column) {
        case COLNAME:
            return "Name";

        case COLECTS:
            return "ECTS";

        default:
            final IResultType type = resultTypes.get(column - COLGRADES);
            return type.toString();
        }
    }

    @Override
    public String getText(final Object data, final int column)
    {
        if (data instanceof AdaptedModule) {
            return getModuleText((AdaptedModule) data, column);
        }

        // TODO
        // if (data instanceof IIndividualCourse) {
        // return getIndividualCourseText((IndividualCourse) data, column);
        // }

        if (data instanceof ICourse) {
            return getCourseText((ICourse) data, column);
        }

        if (data instanceof IResult) {
            return getExaminationResultText((IResult) data, column);
        }

        if (data instanceof AdaptedRegulations) {
            return getOverallText(column);
        }

        return getUnassignedText(column);
    }

    private static String getExaminationResultData(final IResultType type, final IResult examRes)
    {
        if (!type.isPassed()) {
            return "";
        }

        // TODO
        // final String grade = examRes.getGrade(ExaminationResultType.PASSED);
        final String grade = "";
        if (grade.equals("")) {
            return "pass";
        }
        return grade;
    }

    private String getExaminationResultText(final IResult examinationResult, final int column)
    {
        switch (column) {
        case COLNAME:
            if (examinationResult.getCourse() == null) {
                // TODO
                return "TODO";
                // return examinationResult.getCourseIdentifier();
            }
            return examinationResult.getCourse().getName();

        case COLECTS:
            if (examinationResult.getCourse() == null) {
                return "";
            }
            return ECTSUtil.formatECTS(examinationResult.getCourse().getECTS());

        default:
            final IResultType type = resultTypes.get(column - COLGRADES);
            // final ExaminationResult examinationResult =
            // regulations.getStudent().getResult(course.getExamNumber(),
            // course.getIdentifier());
            return getExaminationResultData(type, examinationResult);
        }
    }

    private String getModuleText(final AdaptedModule module, final int column)
    {
        switch (column) {
        case COLNAME:
            return module.getModule().getName();

        case COLECTS:
            return ECTSUtil.formatECTS(module.getModule().getECTS());

        default:
            final IResultType type = resultTypes.get(column - COLGRADES);
            return getModuleData(type, module);
        }
    }

    private String getModuleData(final IResultType type, final AdaptedModule module)
    {
        final String ects;
        final float moduleECTS = module.getECTS(type);
        final boolean allECTS = (moduleECTS >= module.getModule().getECTS());
        if (!allECTS) {
            ects = " (" + ECTSUtil.formatECTS(moduleECTS) + ")";
        }
        else {
            ects = "";
        }

        final String grade;
        if (module.getModule().isGraded()) {
            grade = module.getGrade(type).toString();
        }
        else {
            if (allECTS) {
                grade = PASSED;
            }
            else {
                grade = "";
            }
        }

        final String result = grade + ects;

        if (!type.isPassed()) {
            final String passedResult = getModuleData(ResultTypeListUtil.getPassed(resultTypes), module);
            if (passedResult.equals(result)) {
                return "";
            }
        }

        return result;
    }

    private String getCourseData(final IResultType type, final ICourse course, final IResult examRes)
    {
        if (examRes == null) {
            return "";
        }

        if (course.getUngradedPerformance().isEmpty() && course.getGradedPerformance().isEmpty()) {
            // TODO
            // if (course instanceof IndividualCourse) {
            // return "-";
            // }
            return "-"; // remove
        }

        // if an ungraded performance is required, check if it is passed
        // TODO: think about returning passed grade in braces instead of empty
        // string
        // "(" grade ")"
        if (!course.getUngradedPerformance().isEmpty()) {
            if (!examRes.getPracticalResult(type)) {
                return "";
            }
        }

        final String grade;
        if (!course.getGradedPerformance().isEmpty()) {
            grade = examRes.getGrade(type);
        }
        else {
            grade = PASSED;
        }

        if (!type.isPassed()) {
            // TODO
            final IResultType passedType = ResultTypeListUtil.getPassed(regulations.getStudent().getResultTypes());
            final String passed = getCourseData(passedType, course, examRes);
            if (passed.equals(grade)) {
                return "";
            }
        }

        return grade;
    }

    private String getCourseText(final ICourse course, final int column)
    {
        final IResult examinationResult = regulations.getStudent().getResult(course);

        switch (column) {
        case COLNAME:
            // if ((course instanceof IndividualCourse) &&
            // (examinationResult.getCourseDescription() != null)) {
            // final CourseDescription desc =
            // examinationResult.getCourseDescription();
            // return course.getName() + ": " + desc.getName();
            // }

            return course.getName();

        case COLECTS:
            return ECTSUtil.formatECTS(course.getECTS());

        default:
            final IResultType type = resultTypes.get(column - COLGRADES);
            return getCourseData(type, course, examinationResult);
        }
    }

    private static String getIndividualCourseData(final IResultType type, final ICourse course, // TODO
                                                                                                // IElectiveCourse
            final IResult examRes)
    {
        if (!examRes.getCourse().getUngradedPerformance().isEmpty()) {
            if (!examRes.getPracticalResult(type)) {
                return "";
            }
        }

        final String grade;
        if (!examRes.getCourse().getGradedPerformance().isEmpty()) {
            grade = examRes.getGrade(type);
        }
        else {
            grade = PASSED;
        }

        if (!type.isPassed()) {
            // TODO
            final IResultType passedType = type;
            final String passed = getIndividualCourseData(passedType, course, examRes);
            if (passed.equals(grade)) {
                return "";
            }
        }

        return grade;
    }

    // TODO IElectiveCourse
    private String getIndividualCourseText(final ICourse course, final int column)
    {
        // TODO
        // final ExaminationResult examinationResult =
        // regulations.getStudent().getResult(course.getExamNumber(),
        // course.getIdentifier());
        // if (examinationResult.getCourseDescription() == null) {
        // return getCourseText(course, column);
        // }

        switch (column) {
        case COLNAME:
            // final CourseDescription desc =
            // examinationResult.getCourseDescription();
            // return course.getName() + ": " + desc.getName();
            return course.getName();

        case COLECTS:
            return ECTSUtil.formatECTS(course.getECTS());

        default:
            // final ExaminationResultType type =
            // ExaminationResultType.values()[column - COLGRADES];
            // return getIndividualCourseData(type, course, examinationResult);
            return "";
        }
    }

    private String getOverallText(final int column)
    {
        switch (column) {
        case COLNAME:
            return "Overall";

        case COLECTS:
            return ECTSUtil.formatECTS(regulations.getSPOECTS());

        default:
            final IResultType type = resultTypes.get(column - COLGRADES);
            return getOverallData(type, regulations);
        }
    }

    private String getOverallData(final IResultType type, final AdaptedRegulations studentspo)
    {
        final float reachedECTS = studentspo.getECTS(type);
        final boolean allECTS = (reachedECTS >= studentspo.getSPOECTS());

        final String ects;
        if (!allECTS) {
            ects = " (" + ECTSUtil.formatECTS(reachedECTS) + ")";
        }
        else {
            ects = "";
        }

        final String grade = studentspo.getGrade(type).toString();
        final String result = grade + ects;

        if (!type.isPassed()) {
            final String passedResult = getOverallData(ResultTypeListUtil.getPassed(resultTypes), studentspo);
            if (passedResult.equals(result)) {
                return "";
            }
        }

        return result;
    }

    private String getUnassignedText(final int column)
    {
        switch (column) {
        case COLNAME:
            return "Unassigned (" + regulations.getUnassignedResults().size() + ")";

        default:
            return "";
        }
    }

    @Override
    public boolean isInlineEditorAllowed(final TreeIndex index, final int column)
    {
        // TODO

        return false;

        // if (column < COLGRADES) {
        // return false;
        // }
        //
        // final Object data = getData(index);
        //
        // if (!(data instanceof Course)) {
        // return false;
        // }
        //
        // final Course course = (Course) data;
        //
        // if (!course.getGradedPerformance().isEmpty()) {
        // // no graded performance => no setGrade
        // return false;
        // }
        //
        // final ExaminationResult result =
        // regulations.getStudent().getResult(course);
        // final boolean hasPassedGrade =
        // !result.getGrade(ExaminationResultType.PASSED).equals("");
        // final boolean isPassedGrade = (column == COLGRADES);
        //
        // if ((!isPassedGrade) && (hasPassedGrade)) {
        // // no estimation allowed
        // return false;
        // }
        //
        // return true;
    }

    private boolean isModuleRow(final int row)
    {
        final int firstModuleRow = 1;
        final int lastModuleRow = getModuleCount();

        return ((row >= firstModuleRow) && (row <= lastModuleRow));
    }

    private boolean isOverallRow(final int row)
    {
        return ((row == 0) || (row == getModuleCount() + 1));
    }

    private boolean isUnassignedResultRow(final int row)
    {
        return (row == getModuleCount() + 2);
    }

    @Override
    protected <T> Object getChild(final T item, final int index)
    {
        throw new IllegalStateException("getChild is not implemented in this Model");
    }

    @Override
    public Object getData(final TreeIndex index)
    {
        if (index == null) {
            return null;
        }

        if (index.getParent() == null) {
            if (isOverallRow(index.getRow())) {
                return regulations;
            }

            if (isUnassignedResultRow(index.getRow())) {
                return null;
            }

            if (isModuleRow(index.getRow())) {
                return regulations.getModules().get(index.getRow() - 1);
            }

            return null;
        }

        final int parentRow = index.getParent().getRow();
        if (isModuleRow(parentRow)) {
            // Course
            final int moduleIndex = index.getParent().getRow() - 1;
            final AdaptedModule module = regulations.getModules().get(moduleIndex);
            return module.getModule().getCourses().get(index.getRow());
        }

        if (isUnassignedResultRow(parentRow)) {
            return regulations.getUnassignedResults().get(index.getRow());
        }

        return null;
    }

    private void setGrade(final TreeIndex index, final int column, final String text, final ICourse course)
    {
        // TODO

        // if (column < COLGRADES) {
        // throw new
        // MultiColumnTreeModelException("Only editing grades is allowed");
        // }
        // if (!course.getGradedPerformance().isEmpty()) {
        // // no graded performance => no setGrade
        // return;
        // }
        //
        // final ExaminationResult result =
        // regulations.getStudent().getResult(course);
        // final ExaminationResultType type =
        // ExaminationResultType.values()[column - COLGRADES];
        // final boolean hasPassedGrade =
        // !result.getGrade(ExaminationResultType.PASSED).equals("");
        // final boolean isPassedGrade = (column == COLGRADES);
        //
        // if ((!isPassedGrade) && (hasPassedGrade)) {
        // // no estimation allowed
        // return;
        // }
        //
        // final String newGrade;
        // if ((text == null) || text.equals("")) {
        // newGrade = "";
        // }
        // else {
        // if (!regulations.getGradeFactory().getValidator().isValid(text)) {
        // throw new MultiColumnTreeModelException("'" + text +
        // "' is not a valid grade");
        // }
        //
        // final ICourseGrade grade =
        // regulations.getGradeFactory().createCourseGrade(text, 1);
        // newGrade = grade.getGrade();
        // }
        //
        // final String oldGrade = result.getGrade(type);
        //
        // if (oldGrade.equals(text)) {
        // return;
        // }
        //
        // result.setGrade(type, newGrade);
        //
        // notifyDataChanged(index, column, newGrade);
        // updateOveralls();
    }

    @Override
    public void setText(final TreeIndex index, final int column, final String text)
    {
        final Object data = getData(index);
        if (data instanceof ICourse) {
            setGrade(index, column, text, (ICourse) data);
        }
    }

    private void updateOveralls()
    {
        notifyDataChanged(new TreeIndex(0));
        notifyDataChanged(new TreeIndex(getModuleCount() + 1));
        if (hasUnassignedResults()) {
            notifyDataChanged(new TreeIndex(getModuleCount() + 2));
        }
    }

    private AdaptedModule getStudentModule(final IModule module)
    {
        for (final AdaptedModule studentModule : regulations.getModules()) {
            if (studentModule.getModule() == module) {
                return studentModule;
            }
        }

        return null;
    }

    public void addCourse(final ICourse description)
    {
        // TODO
        // final IndividualModule module = description.getParentModule();
        // final StudentModule studentModule = getStudentModule(module);
        // if (studentModule == null) {
        // throw new IllegalStateException("Illegal module (" + module + ")");
        // }
        // final int modelIndex =
        // regulations.getModules().indexOf(studentModule);
        // module.addCourse(description);
        //
        // final int parentRow = modelIndex + 1;
        //
        // final TreeIndex index = new TreeIndex(parentRow);
        //
        // notifyChildInserted(index, getRowCount(index) - 1);
    }

    public void removeCourse(final ICourse description)
    {
        // TODO
        // final IndividualModule module = description.getParentModule();
        // final StudentModule studentModule = getStudentModule(module);
        // if (studentModule == null) {
        // throw new IllegalStateException("Illegal module (" + module + ")");
        // }
        //
        // final int modelIndex =
        // regulations.getModules().indexOf(studentModule);
        // final int parentRow = modelIndex + 1;
        // final TreeIndex index = new TreeIndex(parentRow);
        //
        // final int courseRow = module.getCourses().indexOf(description);
        //
        // module.removeCourse(description);
        // notifyChildRemoved(index, courseRow);
    }

    // DRAG & DROP
    @Override
    public boolean isDragEnabled()
    {
        return true;
    }

    @Override
    public boolean isDropEnabled()
    {
        return true;
    }

    @Override
    public boolean isDropIndicatorShown()
    {
        return true;
    }

    @Override
    public boolean isDragAllowed(final TreeIndex index, final Object data)
    {
        return (data instanceof IResult);
    }

    @Override
    public boolean isDropAllowed(final Object draggingObject, final TreeIndex index, final Object target)
    {
        if (target == null) {
            return false;
        }

        if (draggingObject instanceof IResult) {
            // TODO
            // if (target instanceof IElectiveCourse) {
            // return false;
            // }

            if (target instanceof AdaptedModule) {
                final AdaptedModule studentModule = (AdaptedModule) target;
                // TODO
                // return (studentModule.getModule() instanceof
                // IndividualModule);
                return false;
            }

            if (target instanceof ICourse) {
                return true;
            }

            return false;
        }

        return false;
    }

    @Override
    public void dropItem(final TreeIndex sourceIndex, final Object draggingObject, final TreeIndex targetIndex,
            final Object target)
    {
        if (draggingObject instanceof IResult) {
            dropItem(sourceIndex, (IResult) draggingObject, targetIndex, target);
        }
    }

    public void dropItem(final TreeIndex sourceIndex, final IResult draggingObject, final TreeIndex targetIndex,
            final Object target)
    {
        if (target instanceof AdaptedModule) {
            dropItem(sourceIndex, draggingObject, targetIndex, (AdaptedModule) target);
        }

        if (target instanceof ICourse) {
            dropItem(sourceIndex, draggingObject, targetIndex, target);
        }
    }

    public void dropItem(final TreeIndex sourceIndex, final IResult examinationResult, final TreeIndex targetIndex,
            final AdaptedModule target)
    {
        // TODO
        // if (target.getModule() instanceof IndividualModule) {
        // final IndividualModule module = (IndividualModule)
        // target.getModule();
        // final ICourseDescriptor course =
        // examinationResult.getCourseDescription();
        // course.setModuleName(module.getName());
        // examinationResult.setCourseIdentifier(module.getIdentifier() +
        // "." + course.getName());
        // module.addCourse(course);
        //
        // final int courseRow = module.getCourses().indexOf(course);
        // // TODO
        // // regulations.removeUnassignedResult(examinationResult);
        // // regulations.getStudent().addResult(examinationResult);
        //
        // final boolean removeUnassigned = !hasUnassignedResults();
        //
        // final TreeIndex newChildIndex = new TreeIndex(courseRow,
        // targetIndex);
        // notifyChildMoved(sourceIndex, newChildIndex);
        // if (removeUnassigned) {
        // notifyChildRemoved(null, getModuleCount() + 2);
        // }
        // }

        // TOOD instanceof Module
    }

    public void dropItem(final TreeIndex sourceIndex, final IResult examinationResult, final TreeIndex targetIndex,
            final IElectiveCourse neighbour)
    {
        // TODO
        // final IndividualModule module = neighbour.getParentModule();
        //
        // // TODO reduce redundancy with (target.getModule() instanceof
        // // IndividualModule)-code
        // final CourseDescriptor course =
        // examinationResult.getCourseDescription();
        // course.setModuleName(module.getName());
        // examinationResult.setCourseIdentifier(module.getIdentifier() + "." +
        // course.getName());
        // module.addCourse(course);
        //
        // final int courseRow = module.getCourses().indexOf(course);
        // // TODO
        // // regulations.removeUnassignedResult(examinationResult);
        // // regulations.getStudent().addResult(examinationResult);
        //
        // final boolean removeUnassigned = !hasUnassignedResults();
        //
        // final TreeIndex newChildIndex = new TreeIndex(courseRow,
        // targetIndex.getParent());
        // notifyChildMoved(sourceIndex, newChildIndex);
        // if (removeUnassigned) {
        // notifyChildRemoved(null, getModuleCount() + 2);
        // }
    }

    public void dropItem(final TreeIndex sourceIndex, final IResult examinationResult, final TreeIndex targetIndex,
            final ICourse course)
    {
        // TODO
        // if (course instanceof CourseDescription) {
        // dropItem(sourceIndex, examinationResult, targetIndex,
        // (CourseDescription) course);
        // return;
        // }
        //
        // // set course result
        // // TODO fuzzy compare
        //
        // boolean query = false;
        // if (examinationResult.getCourseDescription() != null) {
        // if
        // (!course.getName().equals(examinationResult.getCourseDescription().getName()))
        // {
        // query = true;
        // }
        // else if (Math.abs(course.getECTS() -
        // examinationResult.getCourseDescription().getECTS()) > 0.01) {
        // query = true;
        // }
        // }
        // else {
        // query = true;
        // }
        //
        // if (query) {
        // // message box
        // final QMessageBox.StandardButtons buttons = new
        // QMessageBox.StandardButtons();
        // buttons.set(QMessageBox.StandardButton.Ok);
        // buttons.set(QMessageBox.StandardButton.Cancel);
        //
        // final QMessageBox.StandardButton ret = QMessageBox.warning(parent,
        // parent.tr("Grade Calculator"),
        // parent.tr("Course information do not match. Continue anyway?"),
        // buttons,
        // QMessageBox.StandardButton.Ok);
        // if (ret == QMessageBox.StandardButton.Cancel) {
        // return;
        // }
        // }
        //
        // // final ExaminationResult oldResult =
        // // regulations.getStudent().getResult(course);
        // examinationResult.setCourseDescription(null);
        // examinationResult.setCourseIdentifier(course.getIdentifier());
        // // TODO
        // // regulations.getStudent().removeResult(oldResult);
        // // regulations.getStudent().addResult(examinationResult);
        // // regulations.removeUnassignedResult(examinationResult);
        //
        // final boolean removeUnassigned = !hasUnassignedResults();
        //
        // notifyDataChanged(targetIndex);
        // notifyChildRemoved(sourceIndex.getParent(), sourceIndex.getRow());
        // if (removeUnassigned) {
        // notifyChildRemoved(null, getModuleCount() + 2);
        // }
    }

    @Override
    public boolean isRecursiveUpdateAllowed(final TreeIndex row)
    {
        if (row.getParent() != null) {
            return true;
        }

        return isModuleRow(row.getRow());
    }

    @Override
    public void recursiveUpdate(final TreeIndex row)
    {
        if (row == null) {
            updateOveralls();
            return;
        }

        if ((row.getParent() != null) || (isModuleRow(row.getRow()))) {
            super.recursiveUpdate(row);
        }

    }

    public void removeExaminationResult(final IResult examinationResult)
    {
        final int row = regulations.getUnassignedResults().indexOf(examinationResult);
        // TODO
        // regulations.removeUnassignedResult(examinationResult);
        // regulations.getStudent().removeResult(examinationResult);

        notifyChildRemoved(new TreeIndex(getModuleCount() + 2), row);

        if (regulations.getUnassignedResults().size() == 0) {
            notifyChildRemoved(null, getModuleCount() + 2);
        }
    }
}
